package org.xsk.mall.generate;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.*;
import java.util.*;

public class Generator {

    private static final String newRow = "\r\n";

    /**
     * 建立数据库链接
     * @return Connection 数据库链接
     */
    private Connection getConn() throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://192.168.2.12:3306/mall?useUnicode=true&useSSL=false&characterEncoding=utf-8&useSSL=false";
        String userName = "root";
        String userPassword = "root";
        return DriverManager.getConnection(url,userName,userPassword);
    }

    /**
     * 获取指定开头的表名集合
     * @param conn 数据库连接
     * @param first_table_name 表明开头
     * @return 表名集合
     */
    private List<String> getAllTableName(Connection conn,String first_table_name) throws SQLException {
        Statement statement = conn.createStatement();
        ResultSet resultSet = statement.executeQuery("SHOW TABLES;");
        List<String> list = new ArrayList<>();
        while(resultSet.next()){
            String tableName = resultSet.getString(1);
            if (tableName.startsWith(first_table_name)){
                list.add(tableName);
            }
        }
        return list;
    }

    public static void main(String[] args) throws Exception {
        Generator generator = new Generator();
        generator.generate();
    }
    /**
     * 生成实体文件
     */
    public void generate() throws Exception{
        String model = "sms";
        String packagePath = "org.xsk.mall." + model + ".dao.entity";
        String table_name = "sms_home_advertise";
        Connection conn = getConn();

        generateEntity(conn,table_name,packagePath);

        String daoPackagePath = "org.xsk.mall." + model + ".dao.mybatis";
        generateDao(daoPackagePath,table_name,packagePath);

        String managerPath = "org.xsk.mall." + model + ".dao.manager";
        generateManager(managerPath,table_name,packagePath);

        String managerImplPath = "org.xsk.mall." + model + ".dao.manager.impl";
        generateManagerImpl(managerImplPath,table_name,packagePath,daoPackagePath,managerPath);

        String domainPath = "org.xsk.mall." + model + ".domain.service";
        generateDomain(domainPath,table_name);

        String domainImplPath = "org.xsk.mall." + model + ".domain.service.impl";
        generateDomainImpl(domainImplPath,table_name,domainPath);
//        List<String> allTableName = getAllTableName(conn, first_table_name);
//        for (String name : allTableName){
//            generateEntity(conn,name,packagePath);
//        }
    }

    private void generateDomainImpl(String packagePath,String tableName,String domainPath) throws IOException {
        StringBuffer sb = new StringBuffer();
        String domainName = initCap(tableName);
        sb.append("package " + packagePath + ";" + newRow + newRow);
        sb.append("import " + domainPath + "." +  domainName + "Service;" + newRow + newRow);
        sb.append("public class " + domainName + "ServiceImpl implements " + domainName + "Service {");
        sb.append("}");

        String path = System.getProperty("user.dir") + "/src/main/java/" + packagePath.replaceAll("\\.", "/");
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }
        String resPath = path + "/" + domainName + "ServiceImpl" + ".java";
        System.out.println("resPath=" + resPath);
        BufferedWriter writer = new BufferedWriter(new FileWriter(resPath));
        writer.write(sb.toString());
        writer.close();
    }

    private void generateDomain(String packagePath,String tableName) throws IOException {
        StringBuffer sb = new StringBuffer();
        String domainName = initCap(tableName);
        sb.append("package " + packagePath + ";" + newRow + newRow);
        sb.append("public interface " + domainName + "Service {");
        sb.append("}");

        String path = System.getProperty("user.dir") + "/src/main/java/" + packagePath.replaceAll("\\.", "/");
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }
        String resPath = path + "/" + domainName + "Service" + ".java";
        System.out.println("resPath=" + resPath);
        BufferedWriter writer = new BufferedWriter(new FileWriter(resPath));
        writer.write(sb.toString());
        writer.close();
    }
    private void generateManagerImpl(String packagePath,String tableName,String doPackagePath,
                                     String daoPackagePath,String managerPackagePath) throws IOException {
        StringBuffer sb = new StringBuffer();
        String managerImplName = initCap(tableName);
        sb.append("package " + packagePath + ";" + newRow + newRow);
        sb.append("import org.springframework.stereotype.Service;" + newRow);
        sb.append("import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;" + newRow);
        sb.append("import " + doPackagePath + "." + managerImplName + "DO;" + newRow);
        sb.append("import " + daoPackagePath + "." + managerImplName + "Dao;" + newRow);
        sb.append("import " + managerPackagePath + "." + managerImplName + "DaoManager;" + newRow);
        sb.append(newRow);
        sb.append("@Service" + newRow);
        sb.append("public class " + managerImplName + "DaoManagerImpl extends ServiceImpl<" + managerImplName +
                "Dao, " + managerImplName + "DO> implements " + managerImplName + "DaoManager {" + newRow);
        sb.append("}");

        String path = System.getProperty("user.dir") + "/src/main/java/" + packagePath.replaceAll("\\.", "/");
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }
        String resPath = path + "/" + managerImplName + "DaoManagerImpl" + ".java";
        System.out.println("resPath=" + resPath);
        BufferedWriter writer = new BufferedWriter(new FileWriter(resPath));
        writer.write(sb.toString());
        writer.close();
    }
    /**
     * 生成manager对象
     * @param packagePath
     * @param tableName
     * @param doPackagePath
     */
    private void generateManager(String packagePath,String tableName,String doPackagePath) throws IOException {
        StringBuffer sb = new StringBuffer();
        String managerName = initCap(tableName);
        sb.append("package " + packagePath + ";" + newRow + newRow);
        sb.append("import com.baomidou.mybatisplus.extension.service.IService;" + newRow);
        sb.append("import " + doPackagePath + "." + managerName + "DO;" + newRow);
        sb.append(newRow);
        sb.append("public interface " + managerName + "DaoManager extends IService<" + managerName + "DO>{" + newRow);
        sb.append("}");

        String path = System.getProperty("user.dir") + "/src/main/java/" + packagePath.replaceAll("\\.", "/");
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }
        String resPath = path + "/" + managerName + "DaoManager" + ".java";
        System.out.println("resPath=" + resPath);
        BufferedWriter writer = new BufferedWriter(new FileWriter(resPath));
        writer.write(sb.toString());
        writer.close();
    }

    /**
     * 生成mybatis对象
     * @param packagePath DO包路径
     * @param tableName 表名
     */
    private void generateDao(String packagePath,String tableName,String doPackagePath) throws IOException {
        StringBuffer sb = new StringBuffer();
        String daoName = initCap(tableName);
        sb.append("package " + packagePath + ";" + newRow + newRow);
        sb.append("import com.baomidou.mybatisplus.core.mapper.BaseMapper;" + newRow);
        sb.append("import org.apache.ibatis.annotations.Mapper;" + newRow);
        sb.append("import " + doPackagePath + "." + daoName + "DO;" + newRow + newRow);

        sb.append("@Mapper" + newRow);
        sb.append("public interface " + daoName + "Dao extends BaseMapper<" + daoName + "DO> {" + newRow);
        sb.append("}");

        String path = System.getProperty("user.dir") + "/src/main/java/" + packagePath.replaceAll("\\.", "/");
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }
        String resPath = path + "/" + daoName + "Dao" + ".java";
        System.out.println("resPath=" + resPath);
        BufferedWriter writer = new BufferedWriter(new FileWriter(resPath));
        writer.write(sb.toString());
        writer.close();
    }

    /**
     * 生成实体文件
     * @param conn 数据库连接
     * @param tableName 表名
     * @param packagePath 包路径
     */
    private void generateEntity(Connection conn,String tableName,String packagePath) throws SQLException {
        String sql = "SELECT COLUMN_NAME,COLUMN_TYPE,COLUMN_COMMENT " +
                "FROM INFORMATION_SCHEMA.`COLUMNS`  WHERE TABLE_NAME = '" + tableName + "'";
        PreparedStatement stmt = conn.prepareStatement(sql);
        ResultSet resultSet = stmt.executeQuery();

        List<String> colNames = new ArrayList<>();
        Map<String,String> colTypes = new HashMap<>();
        Map<String,String> colComment = new HashMap<>();

        while (resultSet.next()){
            String column_name = resultSet.getString("COLUMN_NAME");
            colNames.add(column_name);
            String column_type = resultSet.getString("COLUMN_TYPE");
            colTypes.put(column_name,column_type);
            String column_comment = resultSet.getString("COLUMN_COMMENT");
            colComment.put(column_name,column_comment);
        }

        try {
            String content = parse(colNames, colTypes,colComment, packagePath, tableName);
            String path = System.getProperty("user.dir") + "/src/main/java/" + packagePath.replaceAll("\\.", "/");
            File file = new File(path);
            if(!file.exists()){
                file.mkdirs();
            }
            String resPath = path+"/" + initCap(tableName) + "DO" + ".java";
            System.out.println("resPath=" + resPath);
            BufferedWriter writer = new BufferedWriter(new FileWriter(resPath));
            writer.write(content);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 数据库表名驼峰转换
     * @param s 带转换字符串
     * @return 准换后的字符串
     */
    //例：user_name --> userName
    private String getCamelStr(String s){
        while(s.indexOf("_")>0){
            int index = s.indexOf("_");
            s = s.substring(0, index) + s.substring(index+1, index+2).toUpperCase() + s.substring(index+2);
        }
        return s;
    }

    /**
     * 解析处理(生成实体类主体代码)
     * @param colNames 字段名集合
     * @param colTypes 字段类型
     * @param packagePath 包路径
     * @param tableName 表名称
     * @return 实体字符串
     */
    private String parse(List<String> colNames, Map<String,String> colTypes,
                         Map<String,String> colComment, String packagePath, String tableName) {
        StringBuffer sb = new StringBuffer();

        sb.append("package ").append(packagePath).append(";").append(newRow).append(newRow);
        parseImport(sb,colTypes.values().toArray());
        
        sb.append("import lombok.Data;" + newRow);
        sb.append("import com.baomidou.mybatisplus.annotation.TableName;" + newRow);

        sb.append(newRow);
        sb.append("@Data" + newRow);
        sb.append("@TableName(value=\"").append(tableName).append("\")").append(newRow);
        sb.append("public class ").append(initCap(tableName)).append("DO").append(" {").append(newRow);

        for (String name : colNames){
            sb.append("    /**" + newRow);
            sb.append("     * ").append(colComment.get(name)).append(newRow);
            sb.append("     */" + newRow);
            sb.append("    private ").append(parseType(colTypes.get(name)))
                    .append(" ").append(getCamelStr(name)).append(";").append(newRow).append(newRow);
        }
        sb.append("}" + newRow);
        return sb.toString();
    }

    /**
     * 数据库字段类型转换class类型
     * @param type 数据库类型
     * @return java类型
     */
    private String parseType(String type){
        if ("DATETIME".equalsIgnoreCase(type) || "TIMESTAMP".equalsIgnoreCase(type)){
            return "Timestamp";
        }else if ("YEAR".equalsIgnoreCase(type) || "DATE".equalsIgnoreCase(type)){
            return "Date";
        }else if ("BLOB".equalsIgnoreCase(type)){
            return "Byte[]";
        }else if ("TIME".equalsIgnoreCase(type)){
            return "Time";
        }else if ("ID".equalsIgnoreCase(type) || "INTEGER".equalsIgnoreCase(type)){
            return "Long";
        }else if ("BOOLEAN".equalsIgnoreCase(type) || "TINYINT".equalsIgnoreCase(type)
                || "SMALLINT".equalsIgnoreCase(type) || "MEDIUMINT".equalsIgnoreCase(type)
                || "INT".equalsIgnoreCase(type)){
            return "Integer";
        }else if ("DECIMAL".equalsIgnoreCase(type)){
            return "BigDecimal";
        }else if ("DOUBLE".equalsIgnoreCase(type)){
            return "Double";
        }else if ("FLOAT".equalsIgnoreCase(type)){
            return "Float";
        }else if ("BIGINT".equalsIgnoreCase(type)){
            return "BigInteger";
        }else if ("BIT".equalsIgnoreCase(type)){
            return "Boolean";
        }else{
            return "String";
        }
    }
    /**
     * 转换导入信息
     * @param sb 实体字符串
     * @param typeList 类型集合
     */
    private void parseImport(StringBuffer sb,Object[] typeList){
        Arrays.stream(typeList).distinct().forEach(object->{
            String type = (String) object;
            if ("DATETIME".equalsIgnoreCase(type) || "TIMESTAMP".equalsIgnoreCase(type)){
                sb.append("import java.sql.Timestamp;" + newRow);
            }else if ("YEAR".equalsIgnoreCase(type) || "DATE".equalsIgnoreCase(type)){
                sb.append("import java.sql.Date;" + newRow);
            }else if ("BLOB".equalsIgnoreCase(type)){
                sb.append("import java.lang.Byte;" + newRow);
            }else if ("TIME".equalsIgnoreCase(type)){
                sb.append("import java.sql.Time;" + newRow);
            }else if ("ID".equalsIgnoreCase(type) || "INTEGER".equalsIgnoreCase(type)){
                sb.append("import java.lang.Long;" + newRow);
            }else if ("BOOLEAN".equalsIgnoreCase(type) || "TINYINT".equalsIgnoreCase(type)
                    || "SMALLINT".equalsIgnoreCase(type) || "MEDIUMINT".equalsIgnoreCase(type)){
                sb.append("import java.lang.Integer;" + newRow);
            }else if ("DECIMAL".equalsIgnoreCase(type)){
                sb.append("import java.math.BigDecimal;" + newRow);
            }else if ("DOUBLE".equalsIgnoreCase(type)){
                sb.append("import java.lang.Double;" + newRow);
            }else if ("FLOAT".equalsIgnoreCase(type)){
                sb.append("import java.lang.Float;" + newRow);
            }else if ("BIGINT".equalsIgnoreCase(type)){
                sb.append("import java.math.BigInteger;" + newRow);
            }else if ("BIT".equalsIgnoreCase(type)){
                sb.append("import java.lang.Boolean;" + newRow);
            }
        });
    }

    /**
     * 把输入字符串的首字母改成大写
     *
     * @param str 待转换字符串
     * @return 转换后的字符串
     */
    private String initCap(String str) {
        char[] ch = str.toCharArray();
        if (ch[0] >= 'a' && ch[0] <= 'z') {
            ch[0] = (char) (ch[0]-32);
        }
        return this.getCamelStr(new String(ch));
    }

}
